﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

/// <summary>
/// A word with its associated letters sorted as well as
/// the letters in that word in a sorted dictionary. Use
/// of this class is recommended if you are looking for a
/// particular word based on letters.
/// </summary>
public class DictionaryEntry
{
    private string word;

    public string Word
    {
        get
        {
            return word;
        }
    }

    private string sortedWord;

    public string SortedWord
    {
        get
        {
            return SortedWord;
        }
    }

    private SortedDictionary<char, int> letters;

    public SortedDictionary<char, int> Letters
    {
        get
        {
            return letters;
        }
    }

    public DictionaryEntry(string word)
    {
        this.word = word.ToLower();
        this.sortedWord = sort(word.ToLower());
        this.letters = mapLetters(word.ToLower());
    }

    private string sort(string str)
    {
        char[] array = str.ToCharArray();
        Array.Sort<char>(array);
        return String.Join("", array);
    }

    private SortedDictionary<char, int> mapLetters(string str)
    {
        SortedDictionary<char, int> letterMap = new SortedDictionary<char, int>();
        char[] array = str.ToLower().ToCharArray();
        foreach (char letter in array)
        {
            if (letterMap.ContainsKey(letter))
            {
                letterMap[letter] = letterMap[letter] + 1;
            }
            else
            {
                letterMap.Add(letter, 1);
            }
        }
        return letterMap;
    }

    /// <summary>
    /// On a scale of 1 to 10 evaluate how similar this word is to the given
    /// string. This evaluation is based off of the size of this word and the
    /// size of the given string as well as a weighting for order. Order is
    /// not considered however if this word is not contained in the othe word.
    /// </summary>
    /// <param name="str">Any string.</param>
    /// <returns>A number from 1 - 10 indicating the similarity index of this
    /// word to the given string.</returns>
    public double similarTo(string str)
    {
        double similarityIndex = 0.0;
        double strLength = Convert.ToDouble(str.Length);
        double wordLength = Convert.ToDouble(Word.Length);
        //Contains
        if (this.word.Contains(str.ToLower()))
        {

            if (Word.Length > str.Length)
            {
                similarityIndex = strLength / wordLength;
            }
            else
            {
                similarityIndex = wordLength / strLength;
            }
        }
        //If our word has letters that are a subset of the other string.
        //This ratio is weighted as we want to consider letters in the 
        //same order as more similar than letters in a different order.
        else if (subsetOfLetters(str))
        {
            if (Word.Length > str.Length)
            {
                similarityIndex = strLength / wordLength * .75;
            }
            else
            {
                similarityIndex = wordLength / strLength * .75;
            }
        }
        return similarityIndex;
    }

    public bool subsetOfLetters(string otherWord)
    {
        SortedDictionary<char, int> otherWordLetters = mapLetters(otherWord.ToLower());
        return subsetOfLetters(otherWordLetters);
    }

    public bool subsetOfLetters(SortedDictionary<char, int> otherWordLetters)
    {
        int numberOfMatchingLetters = 0;
        foreach (KeyValuePair<char, int> item in otherWordLetters)
        {
            if (Letters.ContainsKey(item.Key))
            {
                if (Letters[item.Key] < item.Value)
                {
                    numberOfMatchingLetters += Letters[item.Key];
                }
                else if (Letters[item.Key] >= item.Value)
                {
                    numberOfMatchingLetters += item.Value;
                }
            }
        }
        return numberOfMatchingLetters == Word.Length;
    }

    public override string ToString()
    {
        return word.ToString();
    }
}